apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
  name: d8automountserviceaccounttokenpod
  labels:
    heritage: deckhouse
    module: admission-policy-engine
    security.deckhouse.io: security-policy
  annotations:
    metadata.gatekeeper.sh/title: "Automount Service Account Token for Pod"
    metadata.gatekeeper.sh/version: 1.0.1
    description: >-
      Controls the ability of any Pod to enable automountServiceAccountToken.
spec:
  crd:
    spec:
      names:
        kind: D8AutomountServiceAccountTokenPod
      validation:
        openAPIV3Schema:
          type: object
          description: >-
            Controls the ability of any Pod to enable automountServiceAccountToken.
  targets:
    - target: admission.k8s.gatekeeper.sh
      rego: |
        package d8.security_policies

        import data.lib.exclude_update.is_update

        violation[{"msg": msg}] {
            # spec.automountServiceAccountToken and spec.containers.volumeMounts fields are immutable.
            not is_update(input.review)

            obj := input.review.object
            mountServiceAccountToken(obj.spec)
            msg := sprintf("Automounting service account token is disallowed, pod: %v", [obj.metadata.name])
        }

        mountServiceAccountToken(spec) {
            spec.automountServiceAccountToken == true
        }

        # if there is no automountServiceAccountToken spec, check on volumeMount in containers. Service Account token is mounted on /var/run/secrets/kubernetes.io/serviceaccount
        # https://kubernetes.io/docs/reference/access-authn-authz/service-accounts-admin/#serviceaccount-admission-controller
        mountServiceAccountToken(spec) {
            not has_key(spec, "automountServiceAccountToken")
            "/var/run/secrets/kubernetes.io/serviceaccount" == input_containers[_].volumeMounts[_].mountPath
        }

        input_containers[c] {
            c := input.review.object.spec.containers[_]
        }

        input_containers[c] {
            c := input.review.object.spec.initContainers[_]
        }

        # Ephemeral containers not checked as it is not possible to set field.

        has_key(x, k) {
            _ = x[k]
        }
      libs:
        - |
          package lib.exclude_update

          is_update(review) {
              review.operation == "UPDATE"
          }